package components;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ComponentsMatrix <CC extends CollisionableComponent<?, CC>>{

	private int width;
	private int height;
	private int quadrantSize = 200;
	private double deltaPossX;
	private double deltaPossY;
	
	private List<CC>[][] data;
	
	public ComponentsMatrix (){
		this(1000, 1000, 0, 0);
	}
	public ComponentsMatrix (int width, int height, double deltaPossX, double deltaPossY){
		this.width = width;
		this.height = height;
		this.deltaPossX = deltaPossX;
		this.deltaPossY = deltaPossY;
		this.initData();
	}

	private void initData() {
		data = new ArrayList[this.maxQuadrants()][this.maxQuadrants()];
		
		
		for (int i = 0; i < this.maxQuadrants(); i++){
			for (int ii = 0; ii < this.maxQuadrants(); ii++){
				data[ii][i] = new ArrayList<CC>();
			}
		}
	}
	
	public void addCollisionableComponent(CC gc, double possX, double possY){
		int startX , startY, endX, endY;
		
		startX = (int)(possX+deltaPossX)/quadrantSize;
		startY = (int) (possY+deltaPossY)/quadrantSize;
		endX = (int)(possX+deltaPossX + gc.getDiameter())/this.quadrantSize;
		endY = (int)(possY+deltaPossY + gc.getHeight())/this.quadrantSize;
		
		for (int i = startY; i <= endY; i++){
			for (int ii = startX; ii <= endX; ii++){
				data[ii][i].add(gc);
			}
		}
	}
	public void remove(CC gc, double possX, double possY){
		int startX , startY, endX, endY;
		
		startX = (int)(possX+deltaPossX)/quadrantSize;
		startY = (int) (possY+deltaPossY)/quadrantSize;
		endX = (int)(possX+deltaPossX + gc.getDiameter())/this.quadrantSize;
		endY = (int)(possY+deltaPossY + gc.getHeight())/this.quadrantSize;
		
		for (int i = startY; i <= endY; i++){
			for (int ii = startX; ii <= endX; ii++){
				data[ii][i].remove(gc);
			}
		}
		
	}
	public Set<CC> getComponentsNearSectorExcluding(CC exclude, double x, double y, int width, int height){
		Set<CC > res = new HashSet<CC>();
		
		int startX , startY, endX, endY;
		
		startX = (int)(x+deltaPossX)/quadrantSize;
		startY = (int) (y+deltaPossY)/quadrantSize;
		endX = (int)(x+deltaPossX + width)/this.quadrantSize;
		endY = (int)(y+deltaPossY + height)/this.quadrantSize;
		
		for (int i = startY; i <= endY; i++){
			for (int ii = startX; ii <= endX; ii++){
				res.addAll(data[ii][i]);
			}
		}
		if (res.contains(exclude)){
			res.remove(exclude);
		}
		return res;
	}
	
	public void move(CC gc, double fromX, double fromY, double toX, double toY){
		//remove
		this.remove(gc, fromX, fromY);
		
		//add
		this.addCollisionableComponent(gc, toX, toY);
		
	}
	public int maxQuadrants(){
		return width/quadrantSize;
	}
	public String toString(){
		String st = "";
		for (int i = 0; i < this.width/quadrantSize ;i++){
			for (int ii = 0; ii < this.height/quadrantSize ; ii++){
				st += "[" + data[i][ii].size() + "]";
			}
			st += "\n";
		}
		return st;
		
	}
	public boolean nonSolidCollisionOn(double x, double y, CC comp) {
		double oldX, oldY;
		oldX = comp.getX();
		oldY = comp.getY();
		comp.setX(x);
		comp.setY(y);
		
		Set<CC> ccs = this.getComponentsNearSectorExcluding(comp, x, y, comp.getDiameter(), comp.getHeight());

		for (CC cc : ccs){
			if (comp.collisionNature(cc)){
				comp.setX(oldX);
				comp.setY(oldY);
				return false;
			}
		}
		comp.setX(oldX);
		comp.setY(oldY);
		return true;
	}	
	public boolean nonCollisionOn(double x, double y, CC comp) {
		double oldX, oldY;
		oldX = comp.getX();
		oldY = comp.getY();
		comp.setX(x);
		comp.setY(y);
		
		Set<CC> ccs = this.getComponentsNearSectorExcluding(comp, x, y, comp.getDiameter(), comp.getHeight());

		for (CC cc : ccs){
			if (comp.collisionNature(cc)){
				if (comp.isSolid()){
					comp.setX(oldX);
					comp.setY(oldY);
					return false;	
				}
			}
		}
		comp.setX(oldX);
		comp.setY(oldY);
		return true;
	}

}
